﻿#include "blockgroupmove.h"
#include "block.h"
// #include <QSound>
#include <QGraphicsScene>

BlockGroupMove::BlockGroupMove(const QPointF &point, QObject *parent)
    : BlockGroup(point, parent)
{
    //锁定计时器
    lock_timer = new QTimer(this);
    lock_timer->setInterval(500);
    lock_timer->setSingleShot(true);
    connect(lock_timer, &QTimer::timeout, this, [this]{
        if (!can_move_down()) {
            // qDebug()<<"触底冻结"<<QTime::currentTime();
            // QSound::play(":/audio/freeze.wav");
            ST->soundList()->freeze.play();
            emit need_next_shape();
        }
    });

    //掉落计时器
    drop_timer = new QTimer(this);
    connect(drop_timer, &QTimer::timeout, [this]{
        // qDebug()<<"尝试掉落";
        move_one_step(down);
        if(isCollding()){
            if (m_blockshape == shape_I2 || m_blockshape == shape_I3) {
                deleteBlockFromGroup();
                emit need_next_shape();
            } else {
                move_one_step(up);
                if (!lock_timer->isActive()) {
                    reset_lock_timer();    // 启动锁定计时器
                }
            }            
        }
    });
    connect(this, &BlockGroupMove::game_over, [this]{
        drop_timer->stop();
        setFlags(flags() ^ QGraphicsItem::ItemIsFocusable);       //使得方块组支持键盘操作
    });
    connect(this, &BlockGroupMove::need_next_shape, [this]{
        moveBlockFromGroup();
    });
}

/*
 * @Brief:  游戏开始
 * @Detail: 方块开始下落
 */
void BlockGroupMove::game_start()
{
    setFlags(QGraphicsItem::ItemIsFocusable);       //使得方块组支持键盘操作
    this->setFocus();
    this->show();
    //读取设置的初始游戏速度
    setSpeed(ST->settings()->value("GameSpeed", 1).toInt());
    drop_timer->start();
    this->newShape();           //只有开始时是新建，其余都是复制
}

/**
 * @brief   游戏暂停/继续
 * @details
 */
void BlockGroupMove::game_pause(bool flag)
{
    if (flag) {    // 暂停
        drop_timer->stop();
        setFlags(flags() ^ QGraphicsItem::ItemIsFocusable);    // 使得方块组不支持键盘操作
    } else {       // 继续
        drop_timer->start();
        setFlags(flags() | QGraphicsItem::ItemIsFocusable);    // 使得方块组支持键盘操作
        this->setFocus();
    }
}

/**
 * @brief   游戏中止
 */
void BlockGroupMove::game_end()
{
    qDebug()<<"游戏结束";
    // this->hide();    // 游戏结束，则隐藏下落块
    drop_timer->stop();
    lock_timer->stop();
    setFlags(flags() ^ QGraphicsItem::ItemIsFocusable);    // 使得方块组不支持键盘操作
}

int BlockGroupMove::speed() const
{
    return m_speed;
}

/**
 * @brief   游戏速度设置
 */
void BlockGroupMove::setSpeed(int newSpeed)
{
    m_speed = newSpeed;
    // drop_timer->setInterval(100 * (10 - m_speed));
    drop_timer->setInterval(ST->speedToInterval(m_speed));
}

/*
 * @Brief:  拷贝另一个方块组
 */
void BlockGroupMove::copyShape(BlockGroup *another)
{
    m_blockshape = another->blockshape();
    m_rotation = another->rotation();
    m_ballNum = another->ballNum();     //子弹数量

    deleteBlockFromGroup();
    blockListRemake();
    setRotation(m_rotation);

    setPos(startPos);
    if(isCollding()){
        qDebug()<<"方块冲突";
        do {
            move_one_step(up);
        } while (isCollding());
        // QSound::play(":/audio/gameover.wav");
        ST->soundList()->gameover.play();
        emit game_over();
    }
}

void BlockGroupMove::setKeyPressEventEnable(bool flag)
{
    isKeyPressEventEnable = flag;
}

/**
 * @brief   键盘控制事件
 * @details 旋转碰撞判定，部分采用了ARS旋转系统，
 *          1.依次测试以下三个位置：默认目标位置、向右 1 格、向左 1 格
 *          2.优先向右踢墙
 *          3.I 的踢墙
                条件一：I 正在竖转横
                条件二：I 旁边必须要有墙或砖
                如果这两个条件都成立，就依次测试以下五个位置：默认目标位置、向右 1 格、向左 1 格、向右 2 格、向左 2 格。
 *          4.J、L、T 卡中规则（没有实装）
 */
void BlockGroupMove::keyPressEvent(QKeyEvent *event)
{
   // qDebug()<<"11111"<<(Qt::Key)event->key()<<m_rotation;
    if (isKeyPressEventEnable) {
        switch (event->key()) {
        case Qt::Key_Left:
        case Qt::Key_A:
            if (move_left() && m_blockshape != shape_I2 && m_blockshape != shape_I3) {
                reset_lock_timer();
            }
            break;
        case Qt::Key_Right:
        case Qt::Key_D:
            if (move_right() && m_blockshape != shape_I2 && m_blockshape != shape_I3) {
                reset_lock_timer();
            }
            break;
        case Qt::Key_Up:        //顺时针旋转
        case Qt::Key_W:
            if (m_blockshape == shape_I2) {
                shoot_remove_ball();
            } else if (m_blockshape == shape_I3) {
                shoot_add_ball();
            } else {
                rotation_ARS(90);
            }
            break;
        case Qt::Key_Down:      //下落
        case Qt::Key_S:
            move_down();
            break;
        case Qt::Key_Space:     //快速下落
            if (m_blockshape == shape_I2) {
                shoot_remove_ball();
            } else if (m_blockshape == shape_I3) {
                shoot_add_ball();
            } else {
                // QSound::play(":/audio/down.wav");
                ST->soundList()->down.play();
                do {
                    move_one_step(down);
                } while (!isCollding());
                move_one_step(up);
                emit need_next_shape();
            }
            break;
        case Qt::Key_Z:
        case Qt::Key_Control:           //逆时针旋转
            if (m_blockshape == shape_I2) {
                shoot_remove_ball();
            } else if (m_blockshape == shape_I3) {
                shoot_add_ball();
            } else {
                rotation_ARS(-90);
            }
        default:
            break;
        }
        BlockGroup::keyPressEvent(event);
    }
}

/**
 * @brief   重写焦点丢失函数
 * @details 保证始终获得焦点
 */
void BlockGroupMove::focusOutEvent(QFocusEvent *event)
{
    // qDebug()<<"重新获得焦点!";
    this->setFocus();
    BlockGroup::focusOutEvent(event);
}

/*
 * @Brief:  砖块移动一步
 * @Detail: 只有左、右、下三个方向
 */
void BlockGroupMove::move_one_step(Direction direction)
{
    int d = ST->blockSideLength();
    switch (direction) {
    case up:
        moveBy(0, -d);
        break;
    case down:
        moveBy(0, d);
        break;
    case left:
        moveBy(-d, 0);
        break;
    case right:
        moveBy(d, 0);
        break;
    default:
        break;
    }
}

/**
 * @brief   右移一格
 * @return  true  -- 移动成功
 *          false -- 移动失败
 */
bool BlockGroupMove::move_right()
{
    return move_by_coord(1, 0);
}

/**
 * @brief   左移一格
 */
bool BlockGroupMove::move_left()
{
    return move_by_coord(-1, 0);
}

/**
 * @brief   上移一格
 */
bool BlockGroupMove::move_up()
{
    return move_by_coord(0, -1);
}

/**
 * @brief   下移一格
 */
bool BlockGroupMove::move_down()
{
    return move_by_coord(0, 1);
}

/**
 * @brief   测试能否下落，但不真正移动
 * @return  true  -- 能移动
 *          false -- 不能移动
 */
bool BlockGroupMove::can_move_down()
{
    int d = ST->blockSideLength();
    moveBy(0, d);
    if(isCollding()){
        moveBy(0, -d);
        return false;
    } else {
        moveBy(0, -d);
        return true;
    }
}

/**
 * @brief   尝试按坐标移动
 * @return  true  -- 移动成功
 *          false -- 移动失败
 */
bool BlockGroupMove::move_by_coord(int x, int y)
{
    int d = ST->blockSideLength();
    moveBy(x * d, y *d);
    if(isCollding()){
        moveBy(-x * d, -y * d);
        return false;
    } else {
        return true;
    }
}

void BlockGroupMove::shoot_add_ball()
{
    if (m_ballNum <= 0) {
        return;
    }
    qDebug() << "发射填充弹" << m_ballNum;
    m_ballNum--;
    int d = ST->blockSideLength();
    auto block = new Block(Qt::darkRed, AddBlock);
    block->setPos(this->pos() + QPointF(0, 3 * d));
    if (block->collidingItems().count() > 0) {
        qDebug()<<"发射太快，发射失败";
        delete block;
        return;
    }

    this->scene()->addItem(block);

    auto timer = new QTimer(this);
    connect(timer, &QTimer::timeout, this, [this, block, d, timer] {
        block->moveBy(0, d);
        auto items = block->collidingItems();
        if (items.count() > 0) {
            auto item = items.first();
            if (item->type() == AddBlock || item->type() == RemoveBlock) {
                qDebug() << "消除本砖块";
                delete block;
            } else {
                qDebug() << "放置砖块";
                block->moveBy(0, -d);
                block->setBlockType(NormalBlock);
                emit clear_full_rows();
            }
            timer->stop();
            timer->deleteLater();
        }
    });
    timer->start(25);
}

void BlockGroupMove::shoot_remove_ball()
{
    if (m_ballNum <= 0) {
        return;
    }
    qDebug() << "发射消除弹" << m_ballNum;
    m_ballNum--;
    int d = ST->blockSideLength();
    auto block = new Block(Qt::darkRed, AddBlock);
    block->setPos(this->pos() + QPointF(0, 3 * d));
    if (block->collidingItems().count() > 0) {
        qDebug()<<"发射太快，发射失败";
        delete block;
        return;
    }

    this->scene()->addItem(block);

    auto timer = new QTimer(this);
    connect(timer, &QTimer::timeout, this, [this, block, d, timer] {
        block->moveBy(0, d);
        auto items = block->collidingItems();
        if (items.count() > 0) {
            auto item = items.first();
            if (item->type() == NormalBlock || item->type() == PassModeBlock) {
                // qDebug() << "消除砖块";
                delete item;
                delete block;
            } else {
                // qDebug() << "碰壁结束";
                delete block;
            }
            timer->stop();
            timer->deleteLater();
        }
    });
    timer->start(25);
}

void BlockGroupMove::reset_lock_timer()
{
    if (!can_move_down()) {
        // qDebug()<<"重置锁定计时器"<<QTime::currentTime();
        lock_timer->start();
    }
}

/**
 * @brief   按ARS规则，进行旋转变换
 * @details 无碰撞检测，无踢墙
 *          旋转是可逆的
 * @param   degree -- 旋转角度，一般是90或-90
 */
void BlockGroupMove::rotation_whitout_kick_ARS(int degree)
{
    auto origin_rotation = m_rotation;    // 记录原来的旋转方向
    m_rotation += degree;

    int d = ST->blockSideLength();
    switch (m_blockshape) {
    case shape_I:    // I只允许0°和90°
        if (qAbs(m_rotation) % 360 == 180 || qAbs(m_rotation) % 360 == 0) {
            m_rotation = 0;
            moveBy(-d, 0);    // 需要左移一格，再旋转
            setRotation(m_rotation);
        } else if (qAbs(m_rotation) % 360 == 270 || qAbs(m_rotation) % 360 == 90) {
            m_rotation = 90;
            setRotation(m_rotation);
            moveBy(d, 0);    // 需要旋转后，右移一格
        } else {
            setRotation(m_rotation);
        }
        break;
    case shape_O:    // O只允许0°
        m_rotation = 0;
        setRotation(m_rotation);
        break;
    case shape_T:    // T，J，L在180度时需要下移一格，ARS规则
    case shape_J:
    case shape_L:
    case shape_TR:
        setRotation(m_rotation);
        if (m_rotation % 360 == 180) {
            moveBy(0, d);
        } else if (qAbs(origin_rotation) % 360 == 180) {
            moveBy(0, -d);
        }
        break;
    default:
        setRotation(m_rotation);
        break;
    }
}

/**
 * @brief   按ARS规则，进行旋转变换，并进行踢墙检测
 * @details
 * @param   degree -- 旋转角度，一般是90或-90
 */
void BlockGroupMove::rotation_ARS(int degree)
{
    rotation_whitout_kick_ARS(degree);

    if (isCollding()) {         //开启踢墙环节
        bool kick_flag = false;
        switch (m_blockshape) {
        case shape_I:       //I踢墙规则有不同
            if (move_right()) {
                qDebug() << "右踢一格：成功";
                kick_flag = true;
            } else if (move_left()) {
                qDebug() << "左踢一格：成功";
                kick_flag = true;
            } else if (move_by_coord(2, 0)) {
                qDebug() << "右踢两格：成功";
                kick_flag = true;
            } else if (move_by_coord(-2, 0)) {
                qDebug() << "左踢两格：成功";
                kick_flag = true;
            } else if (move_by_coord(0, 1)) {
                qDebug() << "下踢一格：成功";
                kick_flag = true;
            } else if (move_by_coord(0, -1)) {
                qDebug() << "上踢一格：成功";
                kick_flag = true;
            } else if (move_by_coord(0, 2)) {
                qDebug() << "下踢两格：成功";
                kick_flag = true;
            } else if (move_by_coord(0, -2)) {
                qDebug() << "上踢两格：成功";
                kick_flag = true;
            }
            break;
        case shape_T:
            if (move_right()) {
                qDebug() << "右踢墙：成功";
                kick_flag = true;
            } else if (move_left()) {
                qDebug() << "左踢墙：成功";
                kick_flag = true;
            } else if (move_by_coord(0, -1)) {
                qDebug() << "上踢一格：成功";
                kick_flag = true;
            }
            break;
        default:
            if (move_right()) {
                qDebug() << "右踢墙：成功";
                kick_flag = true;
            } else if (move_left()) {
                qDebug() << "左踢墙：成功";
                kick_flag = true;
            }
            break;
        }
        if (!kick_flag) {
            qDebug() << "踢墙：失败";
            ST->soundList()->kickfailure.play();
            rotation_whitout_kick_ARS(-degree);
        } else {
            // ST->soundList()->kicksuccess.play();
            reset_lock_timer();
        }
    } else {
        reset_lock_timer();
    }
}
